/* Parsing of statements. */

/*
 * compound_statement: '{' '}'
 *      |              '{' statements '}'
 *      ;
 *
 * statements: statement
 *      |      statement statements
 *      ;
 * 
 * statement: print_statement
 *      |     variable_declaration
 *      |     assignment_statement
 *      |     if_statement
 *      |     while_statement
 *      |     for_statement
 *      |     function_decalration
 *      ;
 * 
 * print_statement: 'print' expression 
 *      ;
 * 
 * variable_declaration: 'int' identifier ';'
 *      ;
 * 
 * function_declaration: 'void' identifier '(' ')' compound_statement
 *      ;
 * 
 * assignment_statement: identifier '=' expression
 *      ;
 * 
 * if_statement: if_head
 *      |        if_head 'else' compound_statement
 *      ;
 * 
 * while_statement: 'while' '(' bool_expression ')' compound_statement
 *      ;
 * 
 * for_statement: 'for' '(' preop_statement ';' bool_expression ';'
 *                          postop_statement ')' compound_statement
 *      ;
 * 
 * preop_statement: assignment_statement ';'
 *      ;
 * 
 * postop_statement: assignment_statement ';'
 *      ;
 * 
 * if_head: 'if' '(' bool_expression ')' compound_statement
 *      ;
 * 
 * identifier: T_IDENT
 *      ;
 */

#include "defs.h"
#include "data.h"
#include "decl.h"

static struct ASTnode *print_statement(void)
{
    struct ASTnode *tree;
    int left_type, right_type;

    /* match a 'print' as the first token */
    match(T_PRINT, "print");

    /* parse the following expression */
    tree = binexpr(0);

    /* need to be a int expression */
    left_type = P_INT;
    right_type = tree->type;
    if (!type_compatible(&left_type, &right_type, 0))
        fatal("Incompatible types");

    /* widen if necessary */
    if (right_type) tree = mkastunary(A_WIDEN, P_INT, tree, 0);

    /* make an print AST tree */
    tree = mkastunary(A_PRINT, P_NONE, tree, 0);

    return tree;
}

static struct ASTnode *assignment_statement(void)
{
    struct ASTnode *tree, *left, *right;
    int id, left_type, right_type;

    /* ensure we have an identifier */
    ident();

    /* check it's been defined then make a leaf node for it */
    if ((id = findglob(Text)) == -1)
        fatals("Undeclaraed variable", Text);
    right = mkastleaf(A_LVIDENT, Gsym[id].type, id);

    /* ensure we have an equal sign */
    match(T_ASSIGN, "=");

    /* parse the following expression */
    left = binexpr(0);

    left_type = left->type;
    right_type = right->type;
    if (!type_compatible(&left_type, &right_type, 1))
        fatal("Incompatible types");

    /* widen if necessary */
    if (left_type) left = mkastunary(A_WIDEN, right->type, left, 0);

    /* make an assignment AST tree */
    tree = mkastnode(A_ASSIGN, P_NONE, left, NULL, right, 0);

    return tree;
}

/*
 * Parse an if statement including any
 * optional else clause and return its ast tree.
 */
static struct ASTnode *if_statement(void)
{
    struct ASTnode *cond_ast, *true_ast, *false_ast;

    false_ast = NULL;

    /* ensure we have an 'if' */
    match(T_IF, "if");

    /* require a left parenthesis */
    lparen();

    /* parse the following expression and 
       ensure the tree's operation is comparison */
    cond_ast = binexpr(0);
    if (cond_ast->op < A_EQ || cond_ast->op > A_GE)
        fatal("Bad comparison operator");
    
    /* require a right parenthesis */
    rparen();

    /* get the AST for the compound statement */
    true_ast = compound_statement();

    if (Token.token == T_ELSE) {
        scan(&Token);
        false_ast = compound_statement();
    }

    /* build and return the ast for this statement */
    return mkastnode(A_IF, P_NONE, cond_ast, true_ast, false_ast, 0);
}

static struct ASTnode *while_statement(void)
{
    struct ASTnode *cond_ast, *body_ast;

    /* ensure we have an 'while' */
    match(T_WHILE, "while");

    /* require a left parenthesis */
    lparen();

    /* parse the following expression and
       ensue the tree's operation is comparison */
    cond_ast = binexpr(0);
    if (cond_ast->op < A_EQ || cond_ast->op > A_GE)
        fatal("Bad comparison operator");

    /* require a right parenthesis */
    rparen();

    /* get the AST for the compound statement */
    body_ast = compound_statement();

    /* build and return the ast for this statement */
    return mkastnode(A_WHILE, P_NONE, cond_ast, NULL, body_ast, 0);
}

static struct ASTnode *for_statement(void)
{
    struct ASTnode *preop_ast, *cond_ast, 
                   *body_ast, *postop_ast, *tree;

    /* ensure we have an 'for' */
    match(T_FOR, "for");

    /* require a left parenthesis */
    lparen();

    /* parse the preop_statement */
    preop_ast = assignment_statement();
    semi();

    /* parse the bool expression and 
       ensure the tree's operation is comparison */
    cond_ast = binexpr(0);
    if (cond_ast->op < A_EQ || cond_ast->op > A_GE)
        fatal("Bad comparison operator");
    semi();

    /* parse the postop_statement */
    postop_ast = assignment_statement();

    /* require a right parenthesis */
    rparen();

    /* parse the body */
    body_ast = compound_statement();

    /* glue the compound statement and postop_statement to
       be a new body */
    tree = mkastnode(A_GLUE, P_NONE, body_ast, NULL, postop_ast, 0);

    /* make a while loop with the condition and new body */
    tree = mkastnode(A_WHILE, P_NONE, cond_ast, NULL, tree, 0);
    
    /* and glue the preop_statement to while tree */
    return mkastnode(A_GLUE, P_NONE, preop_ast, NULL, tree, 0);
}

/*
 * Parse one or more statements.
 */
static void statements(struct ASTnode **left_tree)
{
    struct ASTnode *new_tree;

    while (1) {
        switch (Token.token) {
        case T_PRINT:
            new_tree = print_statement();
            break;

        case T_CHAR:
        case T_INT:
            var_declaration();
            new_tree = NULL;
            break;

        case T_IDENT:
            new_tree = assignment_statement();
            break;

        case T_IF:
            new_tree = if_statement();
            break;

        case T_WHILE:
            new_tree = while_statement();
            break;

        case T_FOR:
            new_tree = for_statement();
            break;

        case T_RBRACE:
            return;

        default:
            fatald("Syntax error, token", Token.token);
        }

        if (new_tree) {
            /* some statement must followed by a semicon */
            if (new_tree->op == A_PRINT || new_tree->op == A_ASSIGN)
                semi();

            /* For each new tree, either save it in left if left
               tree is empty, or glue the left and the new tree together. */
            if (*left_tree == NULL)
                *left_tree = new_tree;
            else
                *left_tree = mkastnode(A_GLUE, P_NONE, *left_tree, NULL, new_tree, 0);
        }
    }

}

/*
 * Parse a compound statement and 
 * return its AST node.
 */
struct ASTnode *compound_statement(void)
{
    struct ASTnode *tree;

    tree = NULL;

    /* require a left culy bracket */
    lbrace(); 

    /* parse statements if there is one */
    if (Token.token == T_RBRACE) {
        rbrace();
        return tree;
    }
    statements(&tree);

    /* require a right culy bracket */
    rbrace(); 

    return tree;
}
